package cn.pacerx.running.wx.config;

import static cn.pacerx.running.wx.util.JwtHelper.LOGIN_TOKEN_KEY;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;
import java.util.HashSet;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import cn.pacerx.running.core.util.ResponseUtil;
import cn.pacerx.running.db.util.JsonUtils;
import cn.pacerx.running.wx.service.UserTokenManager;
import cn.pacerx.running.wx.util.JwtHelper;
import cn.pacerx.running.wx.util.UserUtil;
import lombok.RequiredArgsConstructor;

@Component
@RequiredArgsConstructor
public class LoginInterceptor implements HandlerInterceptor {
    private Set<String> directPassUrl = new HashSet<>();
    private final ApplicationContext applicationContext;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 业务逻辑：
        // 排除不需要解析的地址 —— 比如登录地址直接放行
        String uri = request.getRequestURI();
        if (uri.contains("/wx/auth/wechat/login") || uri.contains("/error")) {
            return true;
        }
        if (isDirectPassUrl(request)) {
            return true;
        }
        // 去请求中拿token —— header中token
        String token = request.getHeader(LOGIN_TOKEN_KEY);
        if (StringUtils.isEmpty(token)) {
            writeAuthorizationFailed(response);
            return false;
        }
        Long userId = UserTokenManager.getUserId(token);
        if (userId == null) {
            writeAuthorizationFailed(response);
            return false;
        }

        Date expirationTime = JwtHelper.getExpirationTime(token);
        if (expirationTime.before(new Date())) {
            writeAuthorizationFailed(response);
            return false;
        }

        // 存放到上下文 —— UserUtils
        UserUtil.saveUserId(userId);
        return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        UserUtil.removeUserId();
    }

    /**
     * 封装未授权的返回结果信息
     * @param response 返回response对象
     */
    private void writeAuthorizationFailed(HttpServletResponse response) {
        // 设置未授权状态码
        response.setStatus(HttpStatus.OK.value());
        // 设置响应内容类型为JSON
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        // 设置响应字符编码为UTF-8
        response.setCharacterEncoding("UTF-8");

        // 生成一个表示未授权的响应对象
        Object unlogin = ResponseUtil.unlogin();
        String body = JsonUtils.toJson(unlogin);
        try {
            // 获取 HttpServletResponse 的输出流
            PrintWriter writer = response.getWriter();
            writer.write(body);
            writer.flush();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private boolean isDirectPassUrl(HttpServletRequest request) {
        if (CollectionUtils.isEmpty(directPassUrl)) {
            generateDirectPassUrl();
        }

        String requestPattern = (String) request.getAttribute("org.springframework.web.servlet.HandlerMapping.bestMatchingPattern");
        return directPassUrl.contains(UserUtil.format(requestPattern));
    }


    private void generateDirectPassUrl() {
        RequestMappingHandlerMapping bean = applicationContext.getBean(RequestMappingHandlerMapping.class);
        Map<RequestMappingInfo, HandlerMethod> handlerMethods = bean.getHandlerMethods();
        for (Map.Entry<RequestMappingInfo, HandlerMethod> entry : handlerMethods.entrySet()) {
            RequestMappingInfo requestMappingInfo = entry.getKey();
            Optional.ofNullable(requestMappingInfo.getPatternsCondition()).ifPresent(condition -> {
                Set<String> patterns = condition.getPatterns();
                HandlerMethod handlerMethod = entry.getValue();
                if (handlerMethod.toString().startsWith("cn.pacerx.running.wx.web.WxAuthController")
                        || handlerMethod.toString().startsWith("cn.pacerx.running.wx.web.WxHomeController")) {
                    directPassUrl.addAll(patterns.stream().map(UserUtil::format).collect(Collectors.toList()));
                }
            });
        }
    }
}
